﻿// -----------------------------------------------------------------------
// <copyright file="Nilable.cs" company="Microsoft">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace DotNet37Signals.Client
{
    using System;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    [DataContract(Name = "nilable", Namespace = "")]
    [Serializable, StructLayout(LayoutKind.Sequential)]
    public struct Nillable<T> where T : struct
    {
        private bool hasValue;
        internal T value;
        
        public Nillable(T value)
        {
            this.value = value;
            this.hasValue = true;
        }

        public Nillable(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                this.value = (T)Convert.ChangeType(value, typeof(T)); ;
                this.hasValue = true;
            }
            else
            {
                this.value = default(T);
                this.hasValue = false;
            }
        }

        [IgnoreDataMember]
        public bool HasValue
        {
            get
            {
                return this.hasValue;
            }
        }

        [IgnoreDataMember]
        public T Value
        {
            get
            {
                if (!this.HasValue)
                {
                    throw new InvalidOperationException("No value");
                }
                return this.value;
            }
        }
        
        public T GetValueOrDefault()
        {
            return this.value;
        }

        public T GetValueOrDefault(T defaultValue)
        {
            if (!this.HasValue)
            {
                return defaultValue;
            }
            return this.value;
        }

        public override bool Equals(object other)
        {
            if (!this.HasValue)
            {
                return (other == null);
            }
            if (other == null)
            {
                return false;
            }
            return this.value.Equals(other);
        }

        public override int GetHashCode()
        {
            if (!this.HasValue)
            {
                return 0;
            }
            return this.value.GetHashCode();
        }

        public override string ToString()
        {
            if (!this.HasValue)
            {
                return string.Empty;
            }
            return this.value.ToString();
        }

        public static implicit operator Nillable<T>(string value)
        {
            return new Nillable<T>(value);
        }

        public static implicit operator string(Nillable<T> value)
        {
            return (value.HasValue) ? value.Value.ToString() : string.Empty;
        }

        public static implicit operator Nillable<T>(T value)
        {
            return new Nillable<T>(value);
        }

        public static explicit operator T(Nillable<T> value)
        {
            return value.Value;
        }
    }
}
